package com.liushunqiu.realtor;

import com.alibaba.fastjson2.JSON;
import com.liushunqiu.realtor.annotation.BaseURL;
import com.liushunqiu.realtor.annotation.Controller;
import com.liushunqiu.realtor.annotation.GET;
import com.liushunqiu.realtor.annotation.POST;
import com.liushunqiu.realtor.resolver.HttpParamResolver;
import com.liushunqiu.realtor.resolver.ParamResolver;
import io.vertx.core.MultiMap;
import io.vertx.ext.web.Route;
import io.vertx.ext.web.Router;
import org.reflections.Reflections;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Map;
import java.util.Set;

public class ControllerScanner {
    private String packageToScan;

    private ControllerScanner(String packageToScan) {
        this.packageToScan = packageToScan;
    }

    public static ControllerScanner create(String packageToScan) {
        return new ControllerScanner(packageToScan);
    }

    public void registerControllers(Router router) {
        Reflections reflections = new Reflections(packageToScan);
        Set<Class<?>> controllers = reflections.getTypesAnnotatedWith(Controller.class);

        controllers.stream()
            .map(this::createControllerInstance)
            .forEach(controllerInstance -> registerControllerRoutes(
                controllerInstance,
                controllerInstance.getClass().getAnnotation(BaseURL.class),    // 添加@BaseURL的使用
                router
            ));
    }

    private Object createControllerInstance(Class<?> controllerClass) {
        // 创建类的实例
        try {
            return controllerClass.getDeclaredConstructor().newInstance();
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
                 NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

    private void registerControllerRoutes(Object controller, BaseURL baseURL, Router router) {
        String baseUrl = baseURL != null ? baseURL.base() : "";

        Method[] methods = controller.getClass().getDeclaredMethods();

        for (Method method : methods) {
            if (method.isAnnotationPresent(GET.class)) {
                registerGetRoute(controller, method, router, baseUrl);
            }
            if (method.isAnnotationPresent(POST.class)) {
                registerPostRoute(controller, method, router, baseUrl);
            }
        }
    }

    private void registerGetRoute(Object instance, Method method, Router router, String baseUrl) {
        GET get = method.getAnnotation(GET.class);
        Route route = router.get(baseUrl + get.path());
        // 获取方法的所有参数
        Parameter[] parameters = method.getParameters();
        route.handler(context -> {
            Object resultWithParam;
            method.setAccessible(true);
            try {
                ParamResolver paramResolver = new HttpParamResolver(context.queryParams());
                Object[] args = new Object[parameters.length];
                for (int i = 0; i < parameters.length; i++) {
                    args[i] = paramResolver.resolve(method, i);
                }
                resultWithParam = method.invoke(instance, args);
            } catch (IllegalAccessException | InvocationTargetException e) {
                throw new RuntimeException(e);
            }
            context.response()
                .setStatusCode(200)
                .end(JSON.toJSONString(resultWithParam));
        });
    }

    private void registerPostRoute(Object instance, Method method, Router router, String baseUrl) {
        POST post = method.getAnnotation(POST.class);
        Route route = router.post(baseUrl + post.path());
        // 获取方法的所有参数
        Parameter[] parameters = method.getParameters();
        route.handler(context -> {
            Object resultWithParam;
            method.setAccessible(true);
            try {
                if (parameters.length > 0) {
                    resultWithParam = method.invoke(instance, JSON.parseObject(context.body().asString(), parameters[0].getType()));
                } else {
                    resultWithParam = method.invoke(instance);
                }
            } catch (IllegalAccessException | InvocationTargetException e) {
                throw new RuntimeException(e);
            }
            context.response()
                .setStatusCode(200)
                .end(JSON.toJSONString(resultWithParam));
        });
    }
}
